using System;
using System.Collections;

namespace Meta {
	public class Apropos {
		public static bool IsApropos(GlobalVariable v, string topic) {
			return v.isDefined && (IsApropos(v.name.name, topic) || IsApropos(v.documentation, topic));
		}

		class ResultDisplay : IInspectable {
			ArrayList vars;
			string topic;

			public ResultDisplay(string topic, ArrayList vars) {
				this.topic = topic;
				this.vars = vars;
			}

			string Meta.IInspectable.InspectorName() {
				return "Apropos \""+topic+"\"";
			}

			IList IInspectable.RenderForInspection() {
				InspectorField[] results = new InspectorField[vars.Count];
				//ArrayList results = new ArrayList[vars.Count];

				for (int i=0; i<vars.Count; i++) {
					GlobalVariable v = (GlobalVariable)vars[i];

					results[i] = new InspectorField(v.name.name, v.value, v.documentation==null?"":v.documentation);
				}

				return results;
			}
		}

		// Return true if topic is a substring of info, ignoring case.
		// This could be optimized noticably.
		public static bool IsApropos(string info, string topic) {
			if (info==null)
				return false;
			for (int start=0;  start<=info.Length-topic.Length;  start++) {
				for (int i=0;  i<topic.Length;  i++) {
					char tchar = topic[i];
					char ichar = info[start+i];
					if (char.ToLower(tchar)!=char.ToLower(ichar))
						goto fail;
				}
				return true;
			fail: continue;
			}
			return false;
		}

		public static ArrayList GetApropos(string topic) {
			ArrayList results = new ArrayList();
			GetApropos(topic, results, Namespace.CurrentNamespace);
			results.Sort(VarComparer.Compare);
			return results;
		}
		static void GetApropos(string topic, ArrayList results, NamespaceBase ns) {
			foreach (DictionaryEntry e in ns.bindings) {
				GlobalVariable v = e.Value as GlobalVariable;
				if (v != null) {
					if (IsApropos(v, topic))
						results.Add(v);
				}
			}

			foreach (NamespaceBase i in ns.Imports) {
				GetApropos(topic, results, i);
			}

#if APROPOS_SEARCH_TYPES
			foreach (System.Reflection.Assembly a in AppDomain.CurrentDomain.GetAssemblies())
				foreach (Type t in a.GetTypes())
					if (IsApropos(t.Name, topic) && Utilities.IsVisible(t)) {
						// This is a little twisted.  We're supposed to return a list of variables, so we look to see if
						// we already have a variable of this name that's defined, and if not, we import it and return it.
						GlobalVariable v = (GlobalVariable)Parser.Parse(Symbol.Intern(t.Name));
						if (!v.isDefined) {
							v.SetValueB(t, null);
							results.Add(v);
						}
					}
#endif
		}

		class VarComparer : System.Collections.IComparer {
			public static VarComparer Compare = new VarComparer();
			 int System.Collections.IComparer.Compare(object var1, object var2) {
				return String.Compare(((GlobalVariable)var1).name.name, ((GlobalVariable)var2).name.name, true);
			}
		}

		public static void ShowApropos(string topic) {
			Inspector.Inspect(new ResultDisplay(topic, GetApropos(topic)));
		}
	}
}